package project1driver;

import readfile.ReadStringFile;
import skiplist.SkipList;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Scanner;
import dlldynamicset.DLLDynamicSet;
import bstdynamicset.BSTDynamicSet;

public class Driver 
{	
	// Data and set objects
	private static Object[] arrayOfKeys;
	private static SkipList<String, String> skipSet;
	private static BSTDynamicSet<String, String> bstSet;
	private static DLLDynamicSet<String, String> dllSet;
	private static SetData skip_data;
	private static SetData bst_data;
	private static SetData dll_data;
	
	private static char command = '-';
	private static Scanner readInput = new Scanner(System.in);

	private static String dllResult = null;
	private static String skipResult = null;
	private static String bstResult = null;
	private static String keyAndData = null;
	
	/**
	 * Driver method
	 * @param args
	 */
	public static void main(String[] args)
	{		
		skipSet = new SkipList<String, String>();
		bstSet = new BSTDynamicSet<String, String>();
		dllSet = new DLLDynamicSet<String, String>();
		skip_data = new SetData("Skip List");
		bst_data = new SetData("BST");
		dll_data = new SetData("DLL");
		
		if(checkCommandLineArgs(args))
		{
			arrayOfKeys = readFile(args[0]);
			
			while(displayMenu())
			{}
		}
		else
		{
			System.out.println("Error: Invalid command line arguments. " + 
					"Provide a text file as a command line argument.");
		}
	}
	
	private static void runtest()
	{	
		// First generate random indices for elements
		int[] randomElements = new int[10];
		double randomNum;
		
		for(int i = 0; i < 10; i++)
		{
			randomNum = Math.random() * arrayOfKeys.length;
			randomElements[i] = (int) randomNum;
		}
		
		bst_data = bstSet.runtest(arrayOfKeys, randomElements);
		dll_data = dllSet.runtest(arrayOfKeys, randomElements);
		skip_data = skipSet.runtest(arrayOfKeys, randomElements);
		
		printDataTable();
	}
	
	private static boolean displayMenu()
	{	
		readInput.useDelimiter("\n");
		
		System.out.println("BATTLE OF THE DYNAMIC SETS\n");
		
		while(command != 'q')
		{
			System.out.print(
					"\n\n|******   Command Menu   ******|\n\n" +
					"1)  Insert item\n" +
					"2)  Delete item\n" +
					"3)  Get size of list\n" +
					"4)  Get minimum\n" +
					"5)  Get maximum\n" +
					"6)  Find successor\n" +
					"7)  Find predecessor\n" +
					"8)  Print keys\n" +
					"9)  Search for key\n" +
					"0)  Run test\n" +
					"q)  Quit\n\n" +
					": "
					);
			try{
				command = readInput.next().toCharArray()[0];
			}catch(IndexOutOfBoundsException i)
			{
				//System.out.println("Please enter a selection.");
			}
			
			switch(command)
			{
			case '1':
				resetResults();
				
				System.out.print("\n\n|======   INSERT KEY   ======|" + "\n" +
						"Enter node key: "
						);
				keyAndData = readInput.next();
				
				bstSet.insertData(keyAndData, keyAndData);
				
				dllSet.insertData(keyAndData, keyAndData);
				
				skipSet.insertData(keyAndData, keyAndData);
				
				break;
			case '2':
				resetResults();
				
				System.out.print("\n\n|======   DELETE KEY   ======|" + "\n" +
						"Enter key: ");
				keyAndData = readInput.next();
				
				if(dllSet.deleteKey(keyAndData))
				{
					System.out.println("Key: \"" + keyAndData + "\" deleted from DLLSet");
				}
				else
				{
					System.out.println("Key not found in DLLSet. Unable to delete.");
				}
				
				if(skipSet.deleteKey(keyAndData))
				{
					System.out.println("Key: \"" + keyAndData + "\" deleted from SkipSet");
				}
				else
				{
					System.out.println("Key not found in SkipSet. Unable to delete.");
				}
				
				if(bstSet.deleteKey(keyAndData))
				{
					System.out.println("Key: \"" + keyAndData + "\" deleted from BSTSet");
				}
				else
				{
					System.out.println("Key not found in BSTSet. Unable to delete.");
				}
				
				break;
			case '3':
				System.out.print("\n\n|======   SIZE OF SET ======|\n" +
						"DLLSet elements: " + dllSet.size() + "\n" +
						"SkipSet elements: " + skipSet.size() + "\n" +
						"BSTSet elements: " + bstSet.size() + "\n"
						);
				break;
			case '4':
				System.out.print("\n\n|======   GET MINIMUM   ======|\n" +
						"DLLSet minimum key: " + dllSet.minimum() + "\n" +
						"SkipSet minimum key: " + skipSet.minimum() + "\n" +
						"BSTSet minimum key: " + bstSet.minimum() + "\n"
						);
				break;
			case '5':
				System.out.print("\n\n|======   GET MAXIMUM   ======|\n" +
						"DLLSet maximum key: " + dllSet.maximum() + "\n" +
						"SkipSet maximum key: " + skipSet.maximum() + "\n" +
						"BSTSet maximum key: " + bstSet.maximum() + "\n"
						);
				break;
			case '6':
				resetResults();
				
				System.out.print("\n\n|======   FIND SUCCESSOR   ======|\n" +
						"Enter key: "
						);
				
				keyAndData = readInput.next();
				
				dllResult = dllSet.findSuccessor(keyAndData);
				skipResult = skipSet.findSuccessor(keyAndData);
				bstResult = bstSet.findSuccessor(keyAndData);
				
				if(dllResult != null)
				{
					System.out.println("DLLSet successor: " + dllResult);
				}
				else
				{
					System.out.println("DLLSet: Key not found.");
				}
				
				if(skipResult != null)
				{
					System.out.println("SkipSet successor: " + skipResult);
				}
				else
				{
					System.out.println("SkipSet: Key not found");
				}
				
				if(bstResult != null)
				{
					System.out.println("BSTSet successor: " + bstResult);
				}
				else
				{
					System.out.println("BSTSet: Key not found");
				}
				
				break;
			case '7': 
				resetResults();
				
				System.out.print("\n\n|======   FIND PREDECESSOR   ======|\n" +
						"Enter key: "
						);
				keyAndData = readInput.next();
				
				dllResult = dllSet.findPredecessor(keyAndData);
				skipResult = skipSet.findPredecessor(keyAndData);
				bstResult = bstSet.findPredecessor(keyAndData);
				
				if(dllResult != null)
				{
					System.out.println("DLLSet predecessor: " + dllResult);
				}
				else
				{
					System.out.println("DLLSet: Key not found.");
				}
				
				if(skipResult != null)
				{
					System.out.println("SkipSet predecessor: " + skipResult);
				}
				else
				{
					System.out.println("SkipSet: Key not found");
				}
				
				if(bstResult != null)
				{
					System.out.println("BSTSet predecessor: " + bstResult);
				}
				else
				{
					System.out.println("BSTSet: Key not found");
				}

				break;
			case '8':
				System.out.println(
						"\n\n|======   DISPLAY DATA   ======|\n\n" +
						"|------   DLLSet   ------|\n" +
						"--------------------------\n\n" +
						dllSet + "\n\n" +
						
						"|------   SkipSet   ------|\n" +
						"---------------------------\n\n" +
						skipSet + "\n\n" +
						
						"|------   BSTSet   ------|\n" +
						"--------------------------\n\n" +
						bstSet + "\n\n"
						);
				break;
			case '9':
				resetResults();
				
				System.out.print("\n\n|======   SEARCH FOR KEY   ======|\n\n" +
						"Enter a key: "
						);
				keyAndData = readInput.next();
				
				dllResult = dllSet.searchForKey(keyAndData);
				skipResult = skipSet.searchForKey(keyAndData);
				bstResult = bstSet.searchForKey(keyAndData);
				
				if(dllResult != null)
				{
					System.out.println("DLLSet: Found \"" + dllResult + "\"");
				}
				else
				{
					System.out.println("DLLSet: Key not found");
				}
				
				if(skipResult != null)
				{
					System.out.println("SkipSet: Found \"" + skipResult + "\"");
				}
				else
				{
					System.out.println("SkipSet: Key not found");
				}
				
				if(bstResult != null)
				{
					System.out.println("BSTSet: Found \"" + bstResult + "\"");
				}
				else
				{
					System.out.println("BSTSet: Key not found");
				}
				break;
			case '0':
				System.out.println("\n\n|======   RUNTEST   ======|\n\n" + 
						"Please wait. Datasets of 100,000 will take 1-2 minutes. " +
						"Larger datasets will take longer.\n\n");
				runtest();
				break;
				
			case 'q':
				System.out.println("\n==========\nExiting program...\n\n");
				break;
			default:
				System.out.println("Please choose a valid option.");
			}	
		}
		
		return false;
 	}
	
	private static void resetResults()
	{
		dllResult = null;
		skipResult = null;
		bstResult = null;
		keyAndData = null;
	}
	
	private static boolean checkCommandLineArgs(String [] commandLine)
	{
		boolean commandsAreValid = false;
		
		if(commandLine.length == 1)
		{
			commandsAreValid = true;
		}
		
		return commandsAreValid;
	}
	
	/**
	 * Method to print the runtest data table
	 */
	private static void printDataTable()
	{
		int tableWidth = 192;
		String tableHeader = String.format("| %-12s | %-60s | %-60s | %-60s |",
				"", "LL", "SK", "BST");
		
		String row_insert = String.format(
				"| %-12s " +
				"| %-18d / %-18d / %-18d " +
				"| %-18d / %-18d / %-18d " +
				"| %-18d / %-18d / %-18d |"
				, "insert",
				dll_data.insert_results[0], dll_data.insert_results[1], dll_data.insert_results[2],
				skip_data.insert_results[0], skip_data.insert_results[1], skip_data.insert_results[2],
				bst_data.insert_results[0], bst_data.insert_results[1], bst_data.insert_results[2]);
		
		String row_search = String.format(
				"| %-12s " +
				"| %-18d / %-18d / %-18d " +
				"| %-18d / %-18d / %-18d " +
				"| %-18d / %-18d / %-18d |"
				, "search",
				dll_data.search_results[0], dll_data.search_results[1], dll_data.search_results[2],
				skip_data.search_results[0], skip_data.search_results[1], skip_data.search_results[2],
				bst_data.search_results[0], bst_data.search_results[1], bst_data.search_results[2]
				);
		
		String row_predecessor = String.format(
				"| %-12s " +
				"| %-18d / %-18d / %-18d " +
				"| %-18d / %-18d / %-18d " +
				"| %-18d / %-18d / %-18d |"
				, "predecessor",
				dll_data.pred_results[0], dll_data.pred_results[1], dll_data.pred_results[2],
				skip_data.pred_results[0], skip_data.pred_results[1], skip_data.pred_results[2],
				bst_data.pred_results[0], bst_data.pred_results[1], bst_data.pred_results[2]
				);
		
		String row_successor = String.format(
				"| %-12s " +
				"| %-18d / %-18d / %-18d " +
				"| %-18d / %-18d / %-18d " +
				"| %-18d / %-18d / %-18d |"
				, "successor",
				dll_data.succ_results[0], dll_data.succ_results[1], dll_data.succ_results[2],
				skip_data.succ_results[0], skip_data.succ_results[1], skip_data.succ_results[2],
				bst_data.succ_results[0], bst_data.succ_results[1], bst_data.succ_results[2]
				);
		
		String row_minimum = String.format(
				"| %-12s " +
				"| %-60d " +
				"| %-60d " +
				"| %-60d |"
				, "minimum",
				dll_data.timeOfMin,
				skip_data.timeOfMin,
				bst_data.timeOfMin
				);
		
		String row_maximum = String.format(
				"| %-12s " +
				"| %-60d " +
				"| %-60d " +
				"| %-60d |"
				, "maximum",
				dll_data.timeOfMax,
				skip_data.timeOfMax,
				bst_data.timeOfMax
				);
		
		System.out.println("Begin runtest\n\nSize: " + arrayOfKeys.length);
		printHorizontalLine(tableWidth);
		System.out.println(tableHeader);
		
		printHorizontalLine(tableWidth);
		System.out.println(row_insert);
		
		printHorizontalLine(tableWidth);
		System.out.println(row_search);
		
		printHorizontalLine(tableWidth);
		System.out.println(row_predecessor);
		
		printHorizontalLine(tableWidth);
		System.out.println(row_successor);
		
		printHorizontalLine(tableWidth);
		System.out.println(row_minimum);
		
		printHorizontalLine(tableWidth);
		System.out.println(row_maximum);
		
		printHorizontalLine(tableWidth);
		
		System.out.println("\n\nEnd runtest\n");
		
	}
	
	private static void printHorizontalLine(int n)
	{
		n = n + 13;
		for(int i = 0; i < n; i++)
		{
			System.out.print("-");
		}
		System.out.println();
	}
	
	private static Object[] readFile(String filename)
	{
		ArrayList<String> keysFromFile = new ArrayList<String>();
		
		ReadStringFile fileReader = new ReadStringFile(filename);
		
		try
		{
			while(fileReader.hasLine())
			{
				keysFromFile.add(fileReader.readLine());
			}
			fileReader.closeStream();
		}
		catch(IOException io)
		{
			io.printStackTrace();
			io.getMessage();
			System.out.println("Error: Bad file read.");
		}
		
		return keysFromFile.toArray();
	}
	
	private static void printArray()
	{
		for(int i = 0; i < arrayOfKeys.length; i++)
		{
			System.out.println(arrayOfKeys[i]);
		}
		
		System.out.println("\nArray size: " + arrayOfKeys.length);
	}
}
